#include "OfflineOptimumAlgorithm.h"
#include <algorithm>
#include <iostream>

OfflineOptimumAlgorithm::OfflineOptimumAlgorithm(list<Request> requests, bool homing, float time_offset)
{
	this->requests = requests;
	this->homing = homing;
	this->time_offset = time_offset;
}

OfflineOptimumAlgorithm::~OfflineOptimumAlgorithm()
{
}

list<Request> OfflineOptimumAlgorithm::calculate() {
	float minimum_distance = 0.0;
	list<Request> optimal_permutation = list<Request>();
	if (requests.size()==0)
		return optimal_permutation;
	float distance = 0.0;
	do  {
		distance = calculate_track_distance(requests.begin(), requests.end(), time_offset);

		if (distance != -1) { // if permutation is not to be discarded
			if (minimum_distance == 0.0 || distance<minimum_distance) {
				minimum_distance = distance;
				optimal_permutation = requests;
			}
		}
	} while (next_permutation(requests.begin(), requests.end()));
	mark_visited_time(optimal_permutation.begin(), optimal_permutation.end(), time_offset);
	return optimal_permutation;
}

float OfflineOptimumAlgorithm::calculate_track_distance(list<Request>::iterator start, list<Request>::iterator end, float start_time_offset) {
	list<Request>::iterator origin = start;
	list<Request>::iterator actual_position = start;
	start++;
	float actual_time = start_time_offset;
	while (start!=end) {
		float partial_distance = start->get_distance(*actual_position);
		actual_position = start;
		actual_time += partial_distance;
		//if reaching time of current node < release time => discard permutation
		if (actual_time<(start->release_time))
			return -1;
		++start;
	}
	if (homing) {
		for (int i = 0; i < dimensions; i++)
		actual_time += (--start)->get_distance(*origin);
	}
	//cout << endl;
	return actual_time;
}

void OfflineOptimumAlgorithm::mark_visited_time(list<Request>::iterator start, list<Request>::iterator end, float time_offset) {
	float distance = 0.0;
	list<Request>::iterator actual_position = start;
	++start;
	for (; start!=end; ++start) {
		float partial_distance = start->get_distance(*actual_position);
		distance += partial_distance;
		start->visited_time = time_offset + distance/speed;
		++start;
	}
}
